/**
  ******************************************************************************
  * @file    flash.c
  * @author  Puya Application Team
  * @brief   Contains FLASH HW configuration
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2021 Puya Semiconductor.
  * All rights reserved.</center></h2>
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "flash.h"
#include "usart.h"
#include "bootloader.h"
#include "wdg.h"

/**
  * @brief  This function is used to write data in FLASH memory.
  * @param  dwAddr The address where that data will be written.
  * @param  pucDataBuf The data to be written.
  * @param  ucDataLength The length of the data to be written.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Write FLASH operation done
  *          - ERROR:   Write FLASH operation failed or the value of one parameter is not ok
  */
ErrorStatus WriteFlash(uint32_t dwAddr, uint8_t* pucDataBuf, uint8_t ucDataLength)
{
  uint16_t i, j;
  uint16_t wDataLength;
  uint32_t dwOffsetAddr;
  ErrorStatus eResultFlag;

#ifdef ADJUST_ADDR_AND_SIZE
  uint32_t dwOrgAddr;//备份调整前的地址
  uint32_t dwEndAddr;//调整后的结束地址

  wDataLength = ucDataLength + 1;
  /*
    参考uISP工具:
    当地为0x08000000，大小为0xC8时，调整后的起始地址为0x08000000，结束地址为0x080000FF，所以大小为0x100
    当地为0x080000C8，大小为0xC8时，调整后的起始地址为0x08000080，结束地址为0x080001FF，所以大小为0x180
    当地为0x08000190，大小为0xC8时，调整后的起始地址为0x08000180，结束地址为0x0800027F，所以大小为0x100
  */
  dwOrgAddr = dwAddr;//备份调整前的地址

  if (dwAddr&(FLASH_PAGE_SIZE-1))//起始地址不是按照128字节对齐，需要调整
  {
    CLEAR_BIT(dwAddr, (FLASH_PAGE_SIZE-1));//按128字节对方方式调整得到起始地址

    //数据向后偏移(dwOrgAddr-dwAddr)字节, (dwOrgAddr-dwAddr)==(dwOrgAddr&0x7F)
    for (i=0; i<wDataLength; i++)
    {
      pucDataBuf[(dwOrgAddr&(FLASH_PAGE_SIZE-1))+wDataLength-1-i] = pucDataBuf[wDataLength-1-i];
    }

    //因为调整起始地址而补齐的数据从FLASH中读取，这里必须要先数据偏移然后才能读取
    for (i=0; i<(dwOrgAddr&(FLASH_PAGE_SIZE-1)); i++)
    {
      pucDataBuf[i] = M8(dwAddr + i);
    }
  }

  if ((dwOrgAddr&(FLASH_PAGE_SIZE-1))||(wDataLength&(FLASH_PAGE_SIZE-1)))//起始地址/大小不是按照128字节对齐，需要调整
  {
    dwEndAddr = ((dwOrgAddr+wDataLength-1+(FLASH_PAGE_SIZE-1)) & ~(FLASH_PAGE_SIZE-1)) - 1;//按128字节对方方式调整得到结束地址

    //因为调整结束地址而补齐的数据从FLASH中读取
    for (i=((dwOrgAddr&(FLASH_PAGE_SIZE-1))+wDataLength); i<(dwEndAddr - dwAddr + 1); i++)
    {
      pucDataBuf[i] = M8(dwAddr + i);
    }

    wDataLength = dwEndAddr - dwAddr + 1;//大小 = 结束地址-起始地址+1
  }
#else
  wDataLength = ucDataLength + 1;

  if ((wDataLength&(FLASH_PAGE_SIZE-1))||(dwAddr&(FLASH_PAGE_SIZE-1)))
  {
    return ERROR;
  }
#endif

  //1) 检查 FLASH_SR 寄存器 BSY 位，确认没有正在进行的 flash 操作

  //2) 如果没有正在进行的 flash erase 或者 program 操作，则软件读出该 Page 的 32 个
  //word（如果该 page 已有数据存放，则进行该步骤，否则跳过该步骤）

  //3) 向 FLASH_KEYR 寄存器依次写 KEY1 和 KEY2，解除 FLASH_CR 寄存器的保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  //4) 置位 FLASH_CR 寄存器的 PG 位
  SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_PG));

  dwOffsetAddr = 0;
  //5) 向目标地址进行 program 数据的操作，只接受 32bit 的 program
  for (i=0; i<wDataLength/FLASH_PAGE_SIZE; i++)
  {
    for (j=0; j<FLASH_PAGE_SIZE/4; j++)//0x20 = 32
    {
      M32(dwAddr+dwOffsetAddr) = M32(pucDataBuf+dwOffsetAddr);

      dwOffsetAddr += 4;

      if ((FLASH_PAGE_SIZE/4-2) == j)//0x20 - 2 = 30
      {
        //6) 在软件写第 31 个 word 后， 置位 FLASH_CR 寄存器的 PGSTRT
        SET_BIT(FLASH->CR, FLASH_CR_PGSTRT);
      }
    }

    //7) 写第 32 个 word 后
    //8) 等待 FLASH_SR 寄存器的 BSY 位被清零
    while (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)));

    //9) 检查 FLASH_SR 寄存器的 EOP 标志位（当 program 操作已经成功，该位被置位），然后软件清零该位
    if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
    {
      eResultFlag = SUCCESS;
      //清零 EOP 标志, 写 1，清零该位。
      SET_BIT(FLASH->SR, FLASH_SR_EOP);
    }
    else
    {
      eResultFlag = ERROR;
      break;
    }
  }

  //10) 如果不再有 program 操作，则软件清除 PG 位
  CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_PG));

  return eResultFlag;
}

/**
  * @brief  This function is used to write data in Option bytes.
  * @param  dwAddr The address where that data will be written.
  * @param  pucDataBuf The data to be written.
  * @param  ucDataLength The length of the data to be written.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Write Option bytes operation done
  *          - ERROR:   Write Option bytes operation failed or the value of one parameter is not ok
  */
ErrorStatus WriteOption(uint32_t dwAddr, uint8_t* pucDataBuf, uint8_t ucDataLength)
{
  uint16_t i;
  uint32_t dwOPTR1;
  uint32_t dwOPTR2;
  uint32_t dwBANK0_WRPR;
  uint32_t dwBANK1_WRPR;
  uint32_t dwPCROP0SR;
  uint32_t dwPCROP0ER;
  uint32_t dwPCROP1SR;
  uint32_t dwPCROP1ER;
  ErrorStatus eResultFlag;

  dwOPTR1 = FLASH->OPTR1;
  dwOPTR2 = FLASH->OPTR1;
  dwBANK0_WRPR = FLASH->BANK0_WRPR;
  dwBANK1_WRPR = FLASH->BANK1_WRPR;
  dwPCROP0SR = FLASH->PCROP0SR;
  dwPCROP0ER = FLASH->PCROP0ER;
  dwPCROP1SR = FLASH->PCROP1SR;
  dwPCROP1ER = FLASH->PCROP1ER;

  //2) 检查 BSY 位，确认没有正在进行的 Flash 操作

  //1) 用之前描述的步骤，清零 OPTLOCK 位
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH
  if (READ_BIT(FLASH->CR, FLASH_CR_OPTLOCK) != 0x00U)
  {
    WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY1);
    WRITE_REG(FLASH->OPTKEYR, FLASH_OPTKEY2);
  }

  for (i=0; i<ucDataLength+1; i++)
  {
    if ((OPTR1_BASE+0) == (dwAddr+i))//OPTR1[7:0]
    {
      dwOPTR1 &= ~(0x000000FF<<0);
      dwOPTR1 |= (pucDataBuf[i]<<0);
    }
    if ((OPTR1_BASE+1) == (dwAddr+i))//OPTR1[15:8]
    {
      dwOPTR1 &= ~(0x000000FF<<8);
      dwOPTR1 |= (pucDataBuf[i]<<8);
    }
    
    if ((OPTR2_BASE+1) == (dwAddr+i))//OPTR2[7:0]
    {
      dwOPTR2 &= ~(0x000000FF<<8);
      dwOPTR2 |= (pucDataBuf[i]<<8);
    }

    if ((BANK0_WRPR_BASE+0) == (dwAddr+i))//WRPR[7:0]
    {
      dwBANK0_WRPR &= ~(0x000000FF<<0);
      dwBANK0_WRPR |= (pucDataBuf[i]<<0);
    }
    if ((BANK0_WRPR_BASE+1) == (dwAddr+i))//WRPR[15:8]
    {
      dwBANK0_WRPR &= ~(0x000000FF<<8);
      dwBANK0_WRPR |= (pucDataBuf[i]<<8);
    }
    
    if ((BANK1_WRPR_BASE+0) == (dwAddr+i))//WRPR[7:0]
    {
      dwBANK1_WRPR &= ~(0x000000FF<<0);
      dwBANK1_WRPR |= (pucDataBuf[i]<<0);
    }
    if ((BANK1_WRPR_BASE+1) == (dwAddr+i))//WRPR[15:8]
    {
      dwBANK1_WRPR &= ~(0x000000FF<<8);
      dwBANK1_WRPR |= (pucDataBuf[i]<<8);
    }
    
    if ((PCROP0SR_BASE+0) == (dwAddr+i))//PCROP0SR[7:0]
    {
      dwPCROP0SR &= ~(0x000000FF<<0);
      dwPCROP0SR |= (pucDataBuf[i]<<0);
    }
    if ((PCROP0SR_BASE+1) == (dwAddr+i))//PCROP0SR[15:8]
    {
      dwPCROP0SR &= ~(0x000000FF<<8);
      dwPCROP0SR |= (pucDataBuf[i]<<8);
    }
    
    if ((PCROP0ER_BASE+0) == (dwAddr+i))//PCROP0ER[7:0]
    {
      dwPCROP0ER &= ~(0x000000FF<<0);
      dwPCROP0ER |= (pucDataBuf[i]<<0);
    }
    if ((PCROP0ER_BASE+1) == (dwAddr+i))//PCROP0ER[15:8]
    {
      dwPCROP0ER &= ~(0x000000FF<<8);
      dwPCROP0ER |= (pucDataBuf[i]<<8);
    }
    
    if ((PCROP1SR_BASE+0) == (dwAddr+i))//PCROP1SR[7:0]
    {
      dwPCROP1SR &= ~(0x000000FF<<0);
      dwPCROP1SR |= (pucDataBuf[i]<<0);
    }
    if ((PCROP1SR_BASE+1) == (dwAddr+i))//PCROP1SR[15:8]
    {
      dwPCROP1SR &= ~(0x000000FF<<8);
      dwPCROP1SR |= (pucDataBuf[i]<<8);
    }
    
    if ((PCROP1ER_BASE+0) == (dwAddr+i))//PCROP1ER[7:0]
    {
      dwPCROP1ER &= ~(0x000000FF<<0);
      dwPCROP1ER |= (pucDataBuf[i]<<0);
    }
    if ((PCROP1ER_BASE+1) == (dwAddr+i))//PCROP1ER[15:8]
    {
      dwPCROP1ER &= ~(0x000000FF<<8);
      dwPCROP1ER |= (pucDataBuf[i]<<8);
    }
  }

  //3) 向 option bytes 寄存器 FLASH_OPTR1... 写期望的值
  FLASH->OPTR1 = dwOPTR1;
  FLASH->OPTR2 = dwOPTR2;
  FLASH->BANK0_WRPR = dwBANK0_WRPR;
  FLASH->BANK1_WRPR = dwBANK1_WRPR;
  FLASH->PCROP0SR = dwPCROP0SR;
  FLASH->PCROP0ER = dwPCROP0ER;
  FLASH->PCROP1SR = dwPCROP1SR;
  FLASH->PCROP1ER = dwPCROP1ER;

  SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_OPTSTRT));
  M32(OB_BASE) = 0xFFFFFFFF;

  //4) 等待 BSY 位被清零
  while (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)));

  //5) 等待 EOP 拉高，软件清零
  eResultFlag = (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP)) ? SUCCESS : ERROR;

  //清零 EOP 标志, 写 1，清零该位。
  SET_BIT(FLASH->SR, FLASH_SR_EOP);

  CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_OPTSTRT));

  //SET_BIT(FLASH->CR, FLASH_CR_OPTLOCK);//FLASH_OB_Lock
  //如果软件置位 Lock 位，则 OPTLOCK 位也被自动置位

  if (SUCCESS == eResultFlag)
  {
    USART_SendByte(ACK_BYTE);

    //当 FLASH_CR 寄存器中的 OBL_LAUNCH 位被置位, Option byte loading
    SET_BIT(FLASH->CR, FLASH_CR_OBL_LAUNCH);
  }

  return eResultFlag;
}

/**
  * @brief  This function is used to start FLASH bank erase operation.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Bank erase operation done
  *          - ERROR:   Bank erase operation failed or the value of one parameter is not ok
  */
ErrorStatus BankErase(uint8_t bank)
{
  uint32_t mer, adr, banksize;
  ErrorStatus eResultFlag;
  
  banksize = ROM_SIZE / 2;
  
  switch (bank)
  {
    case 1:
      mer = FLASH_CR_MER1;
      adr = FLASH_BASE + (READ_BIT(FLASH->OPTR1, FLASH_OPTR1_BFB) ? 0 : banksize);
      break;
    case 0:
    default:
      mer = FLASH_CR_MER0;
      adr = FLASH_BASE + (READ_BIT(FLASH->OPTR1, FLASH_OPTR1_BFB) ? banksize : 0);
      break;
  }
  
  SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|mer));

  M32(adr) = 0xFFFFFFFF;

  while (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)));

  eResultFlag = (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP)) ? SUCCESS : ERROR;

  SET_BIT(FLASH->SR, FLASH_SR_EOP);

  CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|mer));

  return eResultFlag;
}

/**
  * @brief  This function is used to start FLASH mass erase operation.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Mass erase operation done
  *          - ERROR:   Mass erase operation failed or the value of one parameter is not ok
  */
ErrorStatus MassErase(void)
{
  if (SUCCESS != BankErase(0))
  {
    return ERROR;
  }
  
  if (SUCCESS != BankErase(1))
  {
    return ERROR;
  }

  return SUCCESS;
}

/**
  * @brief  This function is used to erase the specified FLASH pages.
  * @param  *pwDataBuf Pointer to the buffer that contains erase operation options.
  * @param  ucDataLength Size of the Data buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Erase operation done
  *          - ERROR:   Erase operation failed or the value of one parameter is not ok
  */
ErrorStatus PageErase(uint16_t* pwDataBuf, uint8_t ucDataLength, uint8_t ucPageCount)
{
  uint8_t i, j;
  uint32_t dwAddr;
  ErrorStatus eResultFlag = SUCCESS;

  //1) 检查 BSY 位，确认是否没有正在进行的 Flash 操作

  //2) 向 FLASH_KEYR 寄存器依次写 KEY1,KEY2，解除 FLASH_CR 寄存器保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH
  for (i=0; i<ucDataLength+1; i++)
  {
    if (SUCCESS != eResultFlag)
    {
      break;
    }
    for (j=0; j<ucPageCount; j++)
    {
      WDG_Refresh();

      dwAddr = FLASH_BASE+(pwDataBuf[i]*ucPageCount+j)*FLASH_PAGE_SIZE;
      
      if ((((FLASH_OTP_BASE-FLASH_BASE)/FLASH_PAGE_SIZE)&0xFFFF) == pwDataBuf[i]) {
        dwAddr = FLASH_OTP_BASE;
      }
      else {
        if (pwDataBuf[i] > FLASH_PAGE_NB) {
          eResultFlag = ERROR;
          break;
        }
      }
      
      //3) 置位 FLASH_CR 寄存器的 PER 位
      SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_PER));

      //向该 page 写任意数据（必须 32bit 数据）
      M32(dwAddr) = 0xFFFFFFFF;

      //5) 等待 BSY 位被清零
      while (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)));
      
      CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|FLASH_CR_PER));

      //6) 检查 EOP 标志位被置位
      if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
      {
        eResultFlag = SUCCESS;
        //7) 清零 EOP 标志, 写 1，清零该位。
        SET_BIT(FLASH->SR, FLASH_SR_EOP);
      }
      else
      {
        eResultFlag = ERROR;
        break;
      }
    }
  }

  return eResultFlag;
}

/**
  * @brief  This function is used to erase the specified FLASH sectors.
  * @param  *pwDataBuf Pointer to the buffer that contains erase operation options.
  * @param  ucDataLength Size of the Data buffer.
  * @retval An ErrorStatus enumeration value:
  *          - SUCCESS: Erase operation done
  *          - ERROR:   Erase operation failed or the value of one parameter is not ok
  */
ErrorStatus SectorErase(uint16_t* pwDataBuf, uint8_t ucDataLength)
{
  uint8_t i;
  uint32_t dwAddr;
  uint32_t mer = FLASH_CR_SER;
  ErrorStatus eResultFlag;

  //1) 检查 BSY 位，确认是否没有正在进行的 Flash 操作

  //2) 向 FLASH_KEYR 寄存器依次写 KEY1,KEY2，解除 FLASH_CR 寄存器保护
  //统一FLASH_Unlock入口，防止芯片跑飞，误操作FLASH

  for (i=0; i<ucDataLength+1; i++)
  {
    WDG_Refresh();
    
    dwAddr = FLASH_BASE+pwDataBuf[i]*FLASH_SECTOR_SIZE;
    
    if ((((FLASH_OTP_BASE-FLASH_BASE)/FLASH_SECTOR_SIZE)&0xFFFF) == pwDataBuf[i]) {
      mer = FLASH_CR_PER;
      dwAddr = FLASH_OTP_BASE;
    }
    else {
      if (pwDataBuf[i] > FLASH_SECTOR_NB) {
        eResultFlag = ERROR;
        break;
      }
    }
    
    //3) 置位 FLASH_CR 寄存器的 SER 位
    SET_BIT(FLASH->CR, (FLASH_CR_EOPIE|mer));

    //4) 向该 sector 写任意数据
    M32(dwAddr) = 0xFFFFFFFF;

    //5) 等待 BSY 位被清零
    while (READ_BIT(FLASH->SR, (FLASH_SR_BSY0|FLASH_SR_BSY1)));
    
    CLEAR_BIT(FLASH->CR, (FLASH_CR_EOPIE|mer));

    //6) 检查 EOP 标志位被置位
    if (FLASH_SR_EOP == READ_BIT(FLASH->SR, FLASH_SR_EOP))
    {
      eResultFlag = SUCCESS;
      //7) 清零 EOP 标志, 写 1，清零该位。
      SET_BIT(FLASH->SR, FLASH_SR_EOP);
    }
    else
    {
      eResultFlag = ERROR;
      break;
    }
  }

  return eResultFlag;
}
